package com.w3c.pragmatic.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * redis工具类
 *
 * @author wangdong
 * @since 2025/3/7 13:38
 */
@Component
public class RedisUtil {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 设置简单键值
     *
     * @param key   对应的key
     * @param value 对应的值(一般是String或者Object)
     */
    public void setVal(String key, Object value) {
        setVal(key, value, 0, null);
    }

    /**
     * 设置简单键值
     *
     * @param key      对应的key
     * @param value    对应的值(一般是String或者Object)
     * @param expire   有效期
     * @param timeUnit 有效期单位
     */
    public void setVal(String key, Object value, long expire, TimeUnit timeUnit) {
        // 设置键值对
        redisTemplate.opsForValue().set(key, value);
        // 设置有效期
        if (expire > 0 && ObjectUtil.isNotNull(timeUnit)) redisTemplate.expire(key, expire, timeUnit);
    }

    /**
     * 直接返回简单对象
     *
     * @param key 对应的key
     * @return 返回object, ps:无值返回null
     */
    public Object getVal(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 直接返回简单对象
     *
     * @param key 对应的key
     * @return 返回object, ps:无值返回null
     */
    public String getStringVal(String key) {
        return ObjectUtil.isNotNull(redisTemplate.opsForValue().get(key))
                ? (String) redisTemplate.opsForValue().get(key)
                : null;
    }

    /**
     * 直接返回简单对象
     *
     * @param key   对应的key
     * @param clazz 需要转化的对象
     * @return 返回clazz, ps:无值返回null
     */
    public <T> T getClazzVal(String key, Class<T> clazz) {
        return ObjectUtil.isNotNull(redisTemplate.opsForValue().get(key))
                ? clazz.cast(redisTemplate.opsForValue().get(key))
                : null;
    }

    /**
     * 设置List键值
     *
     * @param key   对应的key
     * @param value 对应的值(List)
     */
    public void setListVal(String key, Object value) {
        setListVal(key, value, 0, null);
    }

    /**
     * 设置List键值(元素可以是对象、列表、字符串等)
     *
     * @param key      对应的key
     * @param value    对应的值(List)
     * @param expire   有效期(如果一个key多次set，其中一次设置了expire那么到期后整个key的list都会被清空)
     * @param timeUnit 有效期单位
     */
    public void setListVal(String key, Object value, long expire, TimeUnit timeUnit) {
        // 设置值(rightPush表示新元素往后加)
        redisTemplate.opsForList().rightPush(key, value);
        // 设置有效期
        if (expire > 0 && ObjectUtil.isNotNull(timeUnit)) redisTemplate.expire(key, expire, timeUnit);
    }

    /**
     * 返回list
     *
     * @param key 对应的key
     * @return 返回List<object>, ps:无值返回空列表
     */
    public List<Object> getListVal(String key) {
        ListOperations<String, Object> listOps = redisTemplate.opsForList();
        return listOps.range(key, 0, -1);
    }

    /**
     * 返回list
     *
     * @param key   对应的key
     * @param clazz 需要转化的对象
     * @return 返回List<Class>, ps:无值返回空列表
     */
    public <T> List<T> getListVal(String key, Class<T> clazz) {
        ListOperations<String, Object> listOps = redisTemplate.opsForList();
        List<Object> redisObjList = listOps.range(key, 0, -1);
        return ObjectUtil.isNotNull(redisObjList)
                ? redisObjList.stream()
                .filter(clazz::isInstance)
                .map(clazz::cast)
                .collect(Collectors.toList())
                : new ArrayList<>();
    }

    /**
     * 设置Hash键值
     *
     * @param key       对应的key
     * @param hashKey   map键
     * @param hashValue map值
     */
    public void setHashVal(String key, Object hashKey, Object hashValue) {
        setHashVal(key, hashKey, hashValue, 0, null);
    }

    /**
     * 设置Hash键值
     *
     * @param key       对应的key
     * @param hashKey   map键
     * @param hashValue map值
     * @param expire    有效期
     * @param timeUnit  有效期单位
     */
    public void setHashVal(
            String key, Object hashKey, Object hashValue, long expire, TimeUnit timeUnit) {
        // 设置值（这里可以用putAll(key, map)直接存入一个map）
        redisTemplate.opsForHash().put(key, hashKey, hashValue);
        // 设置有效期
        if (expire > 0 && ObjectUtil.isNotNull(timeUnit)) redisTemplate.expire(key, expire, timeUnit);
    }

    /**
     * 获取当前key对应的Hash键集合
     *
     * @param key 对应的key
     * @return 当前key对应的Hash键集合
     */
    public Set<Object> getHashKeySet(String key) {
        return redisTemplate.opsForHash().keys(key);
    }

    /**
     * 获取当前key对应的Hash键集合
     *
     * @param key 对应的key
     * @return 当前key对应的Hash值列表
     */
    public List<Object> getHashValList(String key) {
        return redisTemplate.opsForHash().values(key);
    }

    /**
     * 获取当前key对应的Hash键集合
     *
     * @param key     对应的key
     * @param hashKey map键
     * @return 当前key和hashKey对应的HashVal
     */
    public Object getHashVal(String key, Object hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }

    /**
     * 获取当前key对应的Hash键集合
     *
     * @param key     对应的key
     * @param hashKey map键
     * @param clazz   泛型
     * @return 当前key和hashKey对应的HashVal
     */
    public <T> T getHashVal(String key, Object hashKey, Class<T> clazz) {
        Object hashValObj = redisTemplate.opsForHash().get(key, hashKey);
        return ObjectUtil.isNotNull(hashValObj) ? clazz.cast(hashValObj) : null;
    }

    /**
     * 获取当前key对应的Hash键集合
     *
     * @param key 对应的key
     * @return 当前key和hashKey对应的HashVal
     */
    public Map<Object, Object> getHashMap(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 获取当前key对应的Hash键集合
     *
     * @param key      对应的key
     * @param valClazz val对应的类型
     * @return 当前key和hashKey对应的HashVal
     */
    public <V> Map<String, V> getHashMap(String key, Class<V> valClazz) {
        return getHashMap(key, String.class, valClazz);
    }

    /**
     * 获取当前key对应的Hash键集合
     *
     * @param key      对应的key
     * @param keyClazz key对应的类型
     * @param valClazz val对应的类型
     * @return 当前key和hashKey对应的HashVal
     */
    public <K, V> Map<K, V> getHashMap(String key, Class<K> keyClazz, Class<V> valClazz) {
        Map<Object, Object> entries = redisTemplate.opsForHash().entries(key);
        if (CollUtil.isEmpty(entries)) return null;
        // 循环转义
        Map<K, V> resultMap = new HashMap<>();
        for (Map.Entry<Object, Object> entry : entries.entrySet()) {
            K classKey = keyClazz.cast(entry.getKey());
            V classVal = valClazz.cast(entry.getValue());
            resultMap.put(classKey, classVal);
        }
        return resultMap;
    }

    /**
     * 设置Set值
     *
     * @param key   对应的key
     * @param value 值
     */
    public void setSetVal(String key, Object value) {
        setSetVal(key, value, 0, null);
    }

    /**
     * 设置Set值
     *
     * @param key      对应的key
     * @param value    值
     * @param expire   有效期
     * @param timeUnit 有效期单位
     */
    public void setSetVal(String key, Object value, long expire, TimeUnit timeUnit) {
        // 设置值
        redisTemplate.opsForSet().add(key, value);
        // 设置有效期
        if (expire > 0 && ObjectUtil.isNotNull(timeUnit)) redisTemplate.expire(key, expire, timeUnit);
    }

    /**
     * 获取当前key对应的某个Set值
     *
     * @param key 对应的key
     * @return 某个Set值
     */
    public Object getSetVal(String key) {
        // 因为无序所以随机取个值
        return redisTemplate.opsForSet().randomMember(key);
    }

    /**
     * 获取当前key对应的某个Set值
     *
     * @param key   对应的key
     * @param clazz 类型
     * @return 某个Set值
     */
    public <T> T getSetVal(String key, Class<T> clazz) {
        // 因为无序所以随机取个值
        Object redisObj = redisTemplate.opsForSet().randomMember(key);
        return ObjectUtil.isNotNull(redisObj) ? clazz.cast(redisObj) : null;
    }

    /**
     * 获取当前key对应的所有Set集合
     *
     * @param key 对应的key
     * @return Set集合
     */
    public Set<Object> getSetAll(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 获取当前key对应的某个Set值
     *
     * @param key   对应的key
     * @param clazz 类型
     * @return 某个Set值
     */
    public <T> Set<T> getSetAll(String key, Class<T> clazz) {
        Set<Object> members = redisTemplate.opsForSet().members(key);
        if (CollUtil.isEmpty(members)) return null;
        // 循环转义
        Set<T> set = new HashSet<>();
        for (Object object : members) set.add(clazz.cast(object));
        return set;
    }

    /**
     * 删除redis
     *
     * @param key 删除键
     */
    public void removeRedis(String key) {
        redisTemplate.delete(key);
    }

    /**
     * 批量删除redis
     *
     * @param keys 删除键集合
     */
    public void removeRedisBatch(List<String> keys) {
        redisTemplate.delete(keys);
    }

    /**
     * redis某个键自增
     *
     * @param key 删除键
     * @return Long 返回值
     */
    public Long increment(String key) {
        return redisTemplate.opsForValue().increment(key);
    }

    /**
     * redis某个键自增
     *
     * @param key   删除键
     * @param delta 步长
     * @return Long 返回值
     */
    public Long increment(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * redis某个键自减
     *
     * @param key 删除键
     * @return Long 返回值
     */
    public Long decrement(String key) {
        return redisTemplate.opsForValue().decrement(key);
    }

    /**
     * redis某个键自减
     *
     * @param key   删除键
     * @param delta 步长
     * @return Long 返回值
     */
    public Long decrement(String key, long delta) {
        return redisTemplate.opsForValue().decrement(key, delta);
    }

}
